/*
 * This file is part of the eccOS Library.
 * Copyright (C) 2007.
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 */
package org.openeccos;

import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Vector;

import nextapp.echo2.app.Alignment;
import nextapp.echo2.app.ApplicationInstance;
import nextapp.echo2.app.Border;
import nextapp.echo2.app.Button;
import nextapp.echo2.app.Component;
import nextapp.echo2.app.ContentPane;
import nextapp.echo2.app.Extent;
import nextapp.echo2.app.FillImage;
import nextapp.echo2.app.Font;
import nextapp.echo2.app.Insets;
import nextapp.echo2.app.Row;
import nextapp.echo2.app.SplitPane;
import nextapp.echo2.app.WindowPane;
import nextapp.echo2.app.event.ActionEvent;
import nextapp.echo2.app.event.ActionListener;
import nextapp.echo2.app.layout.SplitPaneLayoutData;
import nextapp.echo2.contrib.app.effect.AppearEffectCommand;
import nextapp.echo2.contrib.app.effect.FadeEffectCommand;
import nextapp.echo2.extras.app.TransitionPane;

import org.openeccos.dao.EccosRepository;
import org.openeccos.gui.PDQuickle;
import org.openeccos.internal.LoginEvent;
import org.openeccos.internal.PDLoginPanel;
import org.openeccos.internal.PDTaskMenu;
import org.openeccos.model.MTask;
import org.openeccos.model.MUser;
import org.openeccos.util.ILookAndFeel;
import org.openeccos.util.PDUtil;
import org.openeccos.widgets.PDShortcut;

import com.sas.framework.expojo.ModelExposer;

import echopointng.ContentPaneEx;
import echopointng.LabelEx;
import echopointng.MenuBar;
import echopointng.Strut;

/**
 * This class is the centerpiece of the PDWorkspace framework; it provides the
 * desktop where the menu, windows, tasks etc. are displayed
 *
 * This class is not designed for being extended; instead use a plugin-mechanism
 * (perspective) to fill it with life
 */
public final class EccosDesktop extends ContentPane {

	public static ILookAndFeel getCurrentColorScheme() {
		return EccosDesktop.getCurrentPerspective().getLookAndFeel();
	}

	public static EccosPerspective getCurrentPerspective() {
		return getDesktop().perspective;
	}

	public static MUser getCurrentUser() {
		return EccosAppInstance.getActiveUserSession().getUser();
	}

	public static EccosDesktop getDesktop() {
		return ((EccosAppInstance) ApplicationInstance.getActive()).getDesktop();
	}

	protected EccosAppInstance applicationInstance;
	protected ContentPaneEx contentPane;
	protected TransitionPane transitionPane;
	protected PDTaskMenu taskMenu;
	protected Row footerCommandRow;
	protected Hashtable<String, MTask> registeredTasks = new Hashtable<String, MTask>();
	protected PDLoginPanel pnlLogin;
	protected MenuBar menuBar;
	protected EccosPerspective perspective;
	protected Button btnReload;
	protected LabelEx lblLogoImage;
	protected Row menuRow;
	protected SplitPaneLayoutData spld;
	protected LabelEx lblRecycleBin;

	// Usefull when a component must be shown ( rendered ), but must not be visible to the user.
	private ContentPaneEx hiddenContentPaneEx;

	public EccosDesktop(EccosAppInstance appInstance, EccosPerspective perspective) {
		this.applicationInstance = appInstance;
		this.perspective = perspective;
	}

	public void initDesktop() {
		perspective.setDesktop(this);
		try {

			SplitPane footerSplitPane = new SplitPane(SplitPane.ORIENTATION_VERTICAL_BOTTOM_TOP);
			footerSplitPane.setSeparatorPosition(new Extent(33));
			super.add(footerSplitPane);

			SplitPane hiddenSplitPane = new SplitPane();
			hiddenSplitPane.setSeparatorWidth(new Extent(0));
			hiddenSplitPane.setSeparatorPosition(new Extent(0));
			footerSplitPane.add(hiddenSplitPane);

			hiddenContentPaneEx = new ContentPaneEx();
			hiddenSplitPane.add(hiddenContentPaneEx);

			SplitPane footer = new SplitPane();
			footer.setSeparatorPosition(new Extent(350));
			spld = new SplitPaneLayoutData();
			footer.setLayoutData(spld);
			hiddenSplitPane.add(footer);

			footerCommandRow = new Row();
			footerCommandRow.setInsets(new Insets(6, 5, 0, 0));
			footer.add(footerCommandRow);

			btnReload = new Button();
			btnReload.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {
					applicationInstance.restart();
				}
			});
			SplitPaneLayoutData rld = new SplitPaneLayoutData();
			rld.setInsets(new Insets(0, 0, 35, 0));
			rld.setAlignment(new Alignment(Alignment.RIGHT, Alignment.DEFAULT));
			rld.setOverflow(SplitPaneLayoutData.OVERFLOW_HIDDEN);
			btnReload.setLayoutData(rld);
			footer.add(btnReload);

			contentPane = new ContentPaneEx();
			footerSplitPane.add(contentPane);

			TransitionPane tpLogo = new TransitionPane();
			contentPane.add(tpLogo);

			Row backgroundImageRow = new Row();
			backgroundImageRow.setAlignment(new Alignment(Alignment.RIGHT, Alignment.RIGHT));
			tpLogo.add(backgroundImageRow);

			lblLogoImage = new LabelEx();
			lblLogoImage.setInsets(new Insets(0, 60, 40, 0));
			backgroundImageRow.add(lblLogoImage);

			transitionPane = new TransitionPane();
			transitionPane.setType(TransitionPane.TYPE_FADE);
			contentPane.add(transitionPane);

			menuRow = new Row();
			menuRow.add(new Strut(25, 0));
			contentPane.add(menuRow);

			menuBar = new MenuBar();
			menuBar.setDrawOuterBorders(false);
			menuBar.setBorder(new Border(0, null, Border.STYLE_NONE));
			menuBar.setInsets(new Insets(0));
			menuRow.add(menuBar);

			pnlLogin = new PDLoginPanel();
			pnlLogin.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent loginEvent) {
					btnLoginClicked((LoginEvent) loginEvent);
				}
			});
			footerCommandRow.add(pnlLogin);

			perspective.updateMenu();

			lblRecycleBin = new LabelEx(PDUtil.getImage("img/recycle_bin.gif"));
			lblRecycleBin.setOutsets(new Insets(20, 20, 0, 0));
			contentPane.add(lblRecycleBin);

			taskMenu = new PDTaskMenu(this);
			contentPane.add(taskMenu);

			applyLookAndFeel();

			if (getCurrentUser() != null) {
				pnlLogin.updateState(true, getCurrentUser().getLogin());
				refreshTaskDisplay();
			}
			perspective.onInit();

		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	public void addShortcut(final MTask task) {
		if (!task.isShortcut()) {
			return;
		}
		PDShortcut shortcut = new PDShortcut(task, "img/shortcut1.gif");
		contentPane.add(shortcut);
	}

	public void addTaskEntry(MTask task, boolean flash) {
		taskMenu.addTask(task, flash);
	}

	public void addWindow(Component c) {
		if (c instanceof WindowPane) {
			WindowPane wp = (WindowPane) c;
			defaultAddForm(contentPane, wp);
		} else {
			contentPane.add(c);
		}
	}

	// Used to keep the proper Z-Order in the windows. Rules:
	// All windows must inherit from WindowPane (or a common
	// window pane super class), two all windows must be
	// added with this method (which is indirectly called from
	// addWindow
	public void defaultAddForm(Component parent, WindowPane form) {
		int maxZindex = 0;

		for (int i = 0; i < parent.getComponentCount(); i++) {
			Component component = parent.getComponent(i);

			if (!(component instanceof WindowPane)) {
				continue;
			}
			WindowPane curr = (WindowPane) component;

			if (curr.getZIndex() > maxZindex) {
				maxZindex = curr.getZIndex();
			}
		}
		form.setZIndex(maxZindex + 1);
		parent.add(form);
	}

	private void applyLookAndFeel() {
		setBackground(perspective.getLookAndFeel().getBackgroundClear());
		String s = perspective.getLookAndFeel().getFooterLogo();
		if (s != null) {
			if (s.startsWith("!")) {
				btnReload.setText(s.substring(1));
				btnReload.setForeground(perspective.getLookAndFeel().getForeGround());
				btnReload.setFont(new Font(Font.VERDANA, Font.BOLD, new Extent(10)));
				btnReload.setInsets(new Insets(0, 10, 0, 0));
			} else {
				btnReload.setIcon(PDUtil.getImage(perspective.getLookAndFeel().getFooterLogo()));
			}
		}
		
		String backGroundImage = perspective.getLookAndFeel().getDesktopFillImage();
		if (!PDUtil.isEmpty(backGroundImage)) {
			FillImage fi = new FillImage(PDUtil.getImage(backGroundImage));
			contentPane.setBackgroundImage(fi);
			getTaskPane().setBackgroundImage(fi);
		}
		contentPane.setBackground(perspective.getLookAndFeel().getBackgroundClear());
		
		menuBar.setBackground(perspective.getLookAndFeel().getBackgroundDark());
		if (perspective.getLookAndFeel().getDesktopLogo() != null) {
			lblLogoImage.setIcon(PDUtil.getImage(perspective.getLookAndFeel().getDesktopLogo()));
		} else {
			lblLogoImage.setIcon(null);
		}
		menuRow.setBackground(perspective.getLookAndFeel().getBackgroundDark());
		pnlLogin.applyLookAndFeel(perspective.getLookAndFeel());

		if (perspective.getLookAndFeel().getFooterFillImage() != null) {
			spld.setBackgroundImage(new FillImage(PDUtil.getImage(perspective.getLookAndFeel()
					.getFooterFillImage()), null, null, FillImage.REPEAT_HORIZONTAL));
		}
	}

	protected void btnLoginClicked(LoginEvent loginEvent) {
		EccosUserSession userSession = applicationInstance.getUserSession();
		if (userSession.isLoggedIn()) {
			perspective.loggingOut();
			userSession.doLogout();
			cleanDesk();
			registeredTasks.clear();
			perspective.updateMenu();
		} else {
			userSession.doLogin(loginEvent.getLogin(), loginEvent.getPassword());
			if (!userSession.isLoggedIn()) {
				loginEvent.setLoginError(true);
			} else {
				userSession.getUser().setLocale(Locale.GERMAN);
				loginEvent.setLoginError(false);
				refreshTaskDisplay();
				perspective.updateMenu();
			}
		}
	}

	public void cleanDesk() {
		Component[] comps = contentPane.getComponents();
		for (Component c : comps) {
			if (c instanceof PDShortcut || c instanceof PDQuickle || c instanceof WindowPane) {
				contentPane.remove(c);
			}
		}
		taskMenu.clear(); // Clean the menu
	}

	@Override
	public ApplicationInstance getApplicationInstance() {
		return applicationInstance;
	}

	public ContentPane getContentPane() {
		return contentPane;
	}

	public Row getFooterCommandRow() {
		return footerCommandRow;
	}

	public PDTaskMenu getTaskPane() {
		return taskMenu;
	}

	public void refreshTaskDisplay() {
		refreshTaskDisplay(false);
	}

	public void refreshTaskDisplay(boolean flashNewItems) {

		MUser user = applicationInstance.getUserSession().getUser();
		
		EccosRepository repo = (EccosRepository)ModelExposer.get().getRepository(EccosRepository.NAME);
		List<MTask> tasksToDo = new Vector<MTask>();
		for (Object o : repo.findCurrentTasks()) {
			MTask t = (MTask)o;
			if (t.isAssignedToUser(user)) {
				tasksToDo.add(t);
			}
		}

		// check if something has changed - otherwise there is no need to
		// refresh the menu
		boolean anythingChanged = false;
		if (flashNewItems) {
			for (final MTask t : tasksToDo) {
				if (!registeredTasks.containsKey(t.getIdAsString())) {
					anythingChanged = true;
					break;
				}
			}
		} else {
			anythingChanged = true;
		}
		if (!anythingChanged && registeredTasks.size() == tasksToDo.size()) {
			return;
		}

		// remove all shortcuts
		for (Component c : contentPane.getComponents()) {
			if (c instanceof PDShortcut) {
				contentPane.remove(c);
			}
		}

		Collections.sort(tasksToDo); // sort by priority

		Hashtable<String, MTask> newRegisteredTasks = new Hashtable<String, MTask>();
		if (taskMenu != null) {
			taskMenu.clear();
		}
		for (final MTask t : tasksToDo) {
			if (t.isShortcut()) {
				addShortcut(t);
			} else if (registeredTasks.containsKey(t.getIdAsString())) {
				addTaskEntry(t, false); // no flash, this is an old task
			} else {
				addTaskEntry(t, flashNewItems);
			}
			newRegisteredTasks.put(t.getIdAsString(), t);
		}
		registeredTasks = newRegisteredTasks;
	}

	public void showFading(final Component c) {
		showFading(c, true);
	}

	public void showFading(final Component c, boolean removeComponentMenu) {
		cleanDesk();
		try {
			for (Component c2 : transitionPane.getComponents()) {
				FadeEffectCommand fec = new FadeEffectCommand();
				fec.setComponent(c2);
				ApplicationInstance.getActive().enqueueCommand(fec);
			}
			transitionPane.removeAll();

			transitionPane.add(c);

			AppearEffectCommand aec = new AppearEffectCommand();
			aec.setComponent(c);
			ApplicationInstance.getActive().enqueueCommand(aec);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void switchPerspective(EccosPerspective newPerspective) {
		perspective = newPerspective;
		perspective.setDesktop(this);
		contentPane.setBackgroundImage(null);
		taskMenu.setBackgroundImage(null);
		perspective.updateMenu();
		perspective.onInit();
		applyLookAndFeel();
	}

	public void updateForms() {
		// for (PDWindowPane frmAbstract2 : eventFormMap.values()) {
		// frmAbstract2.update();
		// }
	}

	public void updateLoginState() {
		MUser user = getCurrentUser();
		if (user == null) {
			pnlLogin.updateState(false, null);
		} else {
			pnlLogin.updateState(true, user.getLogin());
		}
		perspective.updateMenu();
		refreshTaskDisplay();
	}

	public void showRecycleBin(boolean visible) {
		lblRecycleBin.setVisible(visible);
	}

	public ContentPaneEx getHiddenContentPaneEx() {
		return hiddenContentPaneEx;
	}

	@Override
	@Deprecated
    public final void add(Component c) {
        throw new IllegalArgumentException("Do not use add(), use addWindow() instead");
    }	
	
    public void removeWindow(Component c) {
    	contentPane.remove(c);
    }
}